{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Introduction to atomman: LAMMPS dump file conversions\n",
    "\n",
    "__Lucas M. Hale__, [lucas.hale@nist.gov](mailto:lucas.hale@nist.gov?Subject=ipr-demo), _Materials Science and Engineering Division, NIST_.\n",
    "    \n",
    "[Disclaimers](http://www.nist.gov/public_affairs/disclaimer.cfm) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Introduction<a id='section1'></a>\n",
    "\n",
    "LAMMPS saves atomic positions and other computed per-atom properties in its tabular dump file format. Files in this format can be generated/loaded by atomman using the dump/load 'atom_dump' style.\n",
    "\n",
    "*Updated version 1.2.5:* To support dump files that are partial lists of atoms, this style assigns/recognizes a per-atom property 'atom_id' that corresponds to the atom's LAMMPS ID. \n",
    "\n",
    "- For dump, the LAMMPS IDs in the LAMMPS dump file will be set to atom_id if the property exists. Otherwise, the LAMMPS IDs will be the index of the Atoms arrays plus one.\n",
    "\n",
    "- For load, the atom_id property will be automatically assigned using the LAMMPS ID values."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Library Imports**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "atomman version = 1.4.0\n",
      "Notebook executed on 2021-08-04\n"
     ]
    }
   ],
   "source": [
    "# Standard Python libraries\n",
    "import datetime\n",
    "\n",
    "# http://www.numpy.org/\n",
    "import numpy as np\n",
    "\n",
    "import atomman as am\n",
    "import atomman.unitconvert as uc\n",
    "\n",
    "# Show atomman version\n",
    "print('atomman version =', am.__version__)\n",
    "\n",
    "# Show date of Notebook execution\n",
    "print('Notebook executed on', datetime.date.today())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate test system information (CsCl)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "avect =  [ 3.200,  0.000,  0.000]\n",
      "bvect =  [ 0.000,  3.200,  0.000]\n",
      "cvect =  [ 0.000,  0.000,  3.200]\n",
      "origin = [ 0.000,  0.000,  0.000]\n",
      "natoms = 2\n",
      "natypes = 2\n",
      "symbols = ('Cs', 'Cl')\n",
      "pbc = [ True  True  True]\n",
      "per-atom properties = ['atype', 'pos', 'charge', 'stress']\n",
      "     id |   atype |  pos[0] |  pos[1] |  pos[2]\n",
      "      0 |       1 |   0.000 |   0.000 |   0.000\n",
      "      1 |       2 |   1.600 |   1.600 |   1.600\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>atype</th>\n",
       "      <th>pos[0]</th>\n",
       "      <th>pos[1]</th>\n",
       "      <th>pos[2]</th>\n",
       "      <th>charge</th>\n",
       "      <th>stress[0][0]</th>\n",
       "      <th>stress[0][1]</th>\n",
       "      <th>stress[0][2]</th>\n",
       "      <th>stress[1][0]</th>\n",
       "      <th>stress[1][1]</th>\n",
       "      <th>stress[1][2]</th>\n",
       "      <th>stress[2][0]</th>\n",
       "      <th>stress[2][1]</th>\n",
       "      <th>stress[2][2]</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>-1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   atype  pos[0]  pos[1]  pos[2]  charge  stress[0][0]  stress[0][1]  \\\n",
       "0      1     0.0     0.0     0.0     1.0           0.0           0.0   \n",
       "1      2     1.6     1.6     1.6    -1.0           0.0           0.0   \n",
       "\n",
       "   stress[0][2]  stress[1][0]  stress[1][1]  stress[1][2]  stress[2][0]  \\\n",
       "0           0.0           0.0           0.0           0.0           0.0   \n",
       "1           0.0           0.0           0.0           0.0           0.0   \n",
       "\n",
       "   stress[2][1]  stress[2][2]  \n",
       "0           0.0           0.0  \n",
       "1           0.0           0.0  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Generate box\n",
    "alat = uc.set_in_units(3.2, 'angstrom')\n",
    "box = am.Box(a=alat, b=alat, c=alat)\n",
    "\n",
    "# Generate atoms with atype, pos, charge, and stress properties\n",
    "atype = [1, 2]\n",
    "pos = [[0,0,0], [0.5, 0.5, 0.5]]\n",
    "charge = uc.set_in_units([1, -1], 'e')\n",
    "stress = uc.set_in_units(np.zeros((2, 3, 3)), 'MPa')\n",
    "atoms = am.Atoms(pos=pos, atype=atype, charge=charge, stress=stress)\n",
    "\n",
    "# Build system from box and atoms, and scale atoms\n",
    "system = am.System(atoms=atoms, box=box, scale=True, symbols=['Cs', 'Cl'])\n",
    "\n",
    "# Print system information\n",
    "print(system)\n",
    "system.atoms_df()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. System.dump('atom_dump')<a id='section2'></a>\n",
    "\n",
    "Parameters\n",
    "\n",
    "- **f** (*str or file-like object, optional*) File path or file-like object to write the table to.  If not given, then the table is returned as a string.\n",
    "\n",
    "- **lammps_units** (*str, optional*) The LAMMPS units option associated with the table values.  This is used for the box dimensions and default units for standard dump properties (not compute/fix definitions). \n",
    "\n",
    "- **scale** (*bool, optional*) Flag indicating if atom positions are to be scaled relative to the box (True) or given in absolute Cartesian values (False, default).\n",
    "\n",
    "- **prop_name** (*list, optional*) The Atoms properties to include.  If neither prop_name or prop_info are given, all system properties will be included.\n",
    "\n",
    "- **table_name** (*list, optional*) The dump table column name(s) that correspond to each prop_name.  If not given, the table_name values will be based on the prop_name and shape values.\n",
    "\n",
    "- **shape** (*list, optional*) The shape of each per-atom property.  If not given, will be inferred from the length of each table_name value.\n",
    "\n",
    "- **unit** (*list, optional*) Lists the units for each prop_name as stored in the table.  For a value of None, no conversion will be performed for that property.  For a value of 'scaled', the corresponding table values will be taken in box-scaled units.  If not given, all unit values will be set to None (i.e. no conversions).\n",
    "\n",
    "- **dtype** (*list, optional*) Allows for the data type of each property to be explicitly given. Values of None will infer the data type from the corresponding property values.  If not given, all values will be None.\n",
    "\n",
    "- **prop_info** (*list of dict, optional*) Structured form of property conversion parameters, in which each dictionary in the list corresponds to a single atoms property.  Each dictionary must have a 'prop_name' field, and can optionally have 'table_name', 'shape', 'unit', and 'dtype' fields.\n",
    "\n",
    "- **float_format** (*str, optional*) c-style formatting string for floating point values.  Default value is '%.13f'.\n",
    "\n",
    "- **return_prop_info** (*bool, optional*) Flag indicating if the filled-in prop_info is to be returned.  Having this allows for 1:1 load/dump conversions.  Default value is False (prop_info is not returned).\n",
    "\n",
    "Returns\n",
    "\n",
    "- **content** (*str*) The generated atom_data content.  Only returned if f is None.\n",
    "\n",
    "- **prop_info** (*list of dict*) The filled-in prop_info structure. Only returned if return_prop_info is True."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ITEM: TIMESTEP\n",
      "0\n",
      "ITEM: NUMBER OF ATOMS\n",
      "2\n",
      "ITEM: BOX BOUNDS pp pp pp\n",
      "0.0000000000000 3.2000000000000\n",
      "0.0000000000000 3.2000000000000\n",
      "0.0000000000000 3.2000000000000\n",
      "ITEM: ATOMS id type x y z q stress[0][0] stress[0][1] stress[0][2] stress[1][0] stress[1][1] stress[1][2] stress[2][0] stress[2][1] stress[2][2]\n",
      "1 1 0.0000000000000 0.0000000000000 0.0000000000000 1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000\n",
      "2 2 1.6000000000000 1.6000000000000 1.6000000000000 -1.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000 0.0000000000000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "atom_dump, prop_info = system.dump('atom_dump', return_prop_info=True)\n",
    "\n",
    "print(atom_dump)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Setting return_prop_info = True above returns a list of dicts that provides a mapping between the tabular dump format and the per-atom scalar/vector/tensor properties in atomman.  This is useful for ensuring proper data and units conversion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'prop_name': 'atom_id', 'table_name': ['id'], 'shape': (), 'unit': None, 'dtype': None}\n",
      "{'prop_name': 'atype', 'table_name': ['type'], 'shape': (), 'unit': None, 'dtype': None}\n",
      "{'prop_name': 'pos', 'table_name': ['x', 'y', 'z'], 'shape': (3,), 'unit': 'angstrom', 'dtype': None}\n",
      "{'prop_name': 'charge', 'table_name': ['q'], 'shape': (), 'unit': 'e', 'dtype': None}\n",
      "{'prop_name': 'stress', 'shape': (3, 3), 'table_name': ['stress[0][0]', 'stress[0][1]', 'stress[0][2]', 'stress[1][0]', 'stress[1][1]', 'stress[1][2]', 'stress[2][0]', 'stress[2][1]', 'stress[2][2]'], 'unit': None, 'dtype': None}\n"
     ]
    }
   ],
   "source": [
    "# Show the generated prop_info conversion info\n",
    "for pinfo in prop_info:\n",
    "    print(pinfo)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. atomman.load('atom_dump')<a id='section3'></a>\n",
    "\n",
    "*Updated version 1.2.5:* Note that property atom_id is added that corresponds to the LAMMPS atom ID.\n",
    "\n",
    "Parameters\n",
    "\n",
    "- **data** (*str or file-like object*) The content, file path or file-like object containing the content to read.\n",
    "\n",
    "- **symbols** (*tuple, optional*) Allows the list of element symbols to be assigned during loading.\n",
    "\n",
    "- **lammps_units** (*str*) The LAMMPS units option associated with the parameters.  Default value is 'metal'.\n",
    "\n",
    "- **prop_name** (*list, optional*) The Atoms properties to generate.\n",
    "         \n",
    "- **table_name** (*list, optional*) The table column name(s) that correspond to each prop_name.  If prop_name, table_name and prop_info are not given, prop_name and table_name will be read in from data.\n",
    "        \n",
    "- **shape** (*list, optional*) The shape of each per-atom property.  If not given, will be taken from standard LAMMPS parameter names, or left at () for direct property-table conversion.\n",
    "        \n",
    "- **unit** (*list, optional*) Lists the units for each prop_name as stored in the table.  For a value of None, no conversion will be performed for that property.  For a value of 'scaled', the corresponding table values will be taken in box-scaled units.  If not given, all unit values will be set to None (i.e. no conversions).\n",
    "        \n",
    "- **dtype** (*list, optional*) Allows for the data type of each property to be explicitly given.  Values of None will infer the data type from the corresponding property values.  If not given, all values will be None.\n",
    "        \n",
    "- **prop_info** (*list of dict, optional*) Structured form of property conversion parameters, in which each dictionary in the list corresponds to a single atoms property.  Each dictionary must have a 'prop_name' field, and can optionally have 'table_name', 'shape', 'unit', and 'dtype' fields.\n",
    "        \n",
    "- **return_prop_info** (*bool, optional*) Flag indicating if the full prop_info is to be returned.  Default value is False.\n",
    "        \n",
    "Returns\n",
    "\n",
    "- **system** (*atomman.System*) The generated system.\n",
    "\n",
    "- **prop_info** (*list of dict*) The full prop_info detailing the property-table conversion. Returned if return_prop_info is True."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "avect =  [ 3.200,  0.000,  0.000]\n",
      "bvect =  [ 0.000,  3.200,  0.000]\n",
      "cvect =  [ 0.000,  0.000,  3.200]\n",
      "origin = [ 0.000,  0.000,  0.000]\n",
      "natoms = 2\n",
      "natypes = 2\n",
      "symbols = ('Cs', 'Cl')\n",
      "pbc = [ True  True  True]\n",
      "per-atom properties = ['atype', 'pos', 'atom_id', 'charge', 'stress[0][0]', 'stress[0][1]', 'stress[0][2]', 'stress[1][0]', 'stress[1][1]', 'stress[1][2]', 'stress[2][0]', 'stress[2][1]', 'stress[2][2]']\n",
      "     id |   atype |  pos[0] |  pos[1] |  pos[2]\n",
      "      0 |       1 |   0.000 |   0.000 |   0.000\n",
      "      1 |       2 |   1.600 |   1.600 |   1.600\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>atype</th>\n",
       "      <th>pos[0]</th>\n",
       "      <th>pos[1]</th>\n",
       "      <th>pos[2]</th>\n",
       "      <th>atom_id</th>\n",
       "      <th>charge</th>\n",
       "      <th>stress[0][0]</th>\n",
       "      <th>stress[0][1]</th>\n",
       "      <th>stress[0][2]</th>\n",
       "      <th>stress[1][0]</th>\n",
       "      <th>stress[1][1]</th>\n",
       "      <th>stress[1][2]</th>\n",
       "      <th>stress[2][0]</th>\n",
       "      <th>stress[2][1]</th>\n",
       "      <th>stress[2][2]</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>1</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <td>1</td>\n",
       "      <td>2</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>1.6</td>\n",
       "      <td>2</td>\n",
       "      <td>-1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   atype  pos[0]  pos[1]  pos[2]  atom_id  charge  stress[0][0]  stress[0][1]  \\\n",
       "0      1     0.0     0.0     0.0        1     1.0           0.0           0.0   \n",
       "1      2     1.6     1.6     1.6        2    -1.0           0.0           0.0   \n",
       "\n",
       "   stress[0][2]  stress[1][0]  stress[1][1]  stress[1][2]  stress[2][0]  \\\n",
       "0           0.0           0.0           0.0           0.0           0.0   \n",
       "1           0.0           0.0           0.0           0.0           0.0   \n",
       "\n",
       "   stress[2][1]  stress[2][2]  \n",
       "0           0.0           0.0  \n",
       "1           0.0           0.0  "
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Read in dump file\n",
    "atom_dump_system = am.load('atom_dump', atom_dump, symbols=['Cs', 'Cl'])\n",
    "\n",
    "print(atom_dump_system)\n",
    "atom_dump_system.atoms_df()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
